home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 301-325 / disk_319 / cnewssrc / cnews.orig.lzh / relay / control.c < prev    next >
C/C++ Source or Header  |  1989-06-27  |  7KB  |  244 lines

  1. /*
  2.  * Implement the Usenet control messages, as per RFC 1036 (nee 850).
  3.  * These are fairly infrequent and can afford to be done by
  4.  * separate programs.  They are:
  5.  *
  6.  * cancel message-ID    restricted to Sender: else From: or root, in theory
  7.  * ihave message-ID-list remotesys    generate a sendme from message-ID-list
  8.  * sendme message-ID-list remotesys    send articles named to remotesys
  9.  * (ihave/sendme is semi-documented in the RFCs, kludgey and broken in B2.10.)
  10.  *
  11.  * newgroup groupname    must be Approved:
  12.  * rmgroup groupname    must be Approved:; allow some local control over this
  13.  * sendsys        mail to Reply-To: else From:
  14.  * senduuname        ditto
  15.  * version        ditto
  16.  */
  17.  
  18. #include <stdio.h>
  19. #include <ctype.h>
  20. #include <sys/types.h>
  21.  
  22. #include "libc.h"
  23. #include "news.h"
  24. #include "config.h"
  25. #include "headers.h"
  26. #include "article.h"
  27. #include "caches.h"
  28. #include "history.h"
  29.  
  30. #define NO_FILES ""
  31. #define SUBDIR binfile("ctl")        /* holds shell scripts */
  32.  
  33. /*
  34.  * These are shell meta-characters, except for /, which is included
  35.  * since it allows people to escape from the control directory.
  36.  */
  37. #define SHELLMETAS "<>|&;({$=*?[`'\"/"
  38.  
  39. /* imports from news */
  40. extern statust snufffiles(); 
  41. extern void ihave(), sendme();
  42.  
  43. /* forwards */
  44. FORWARD statust cancelart();
  45. FORWARD void runctlmsg(), bombctlmsg();
  46.  
  47. /*
  48.  * Implement control message specified in "art".
  49.  * Because newgroup and rmgroup may modify the active file, for example,
  50.  * we must flush in-core caches to disk first and reload them afterward.
  51.  * We handle cancels in this process for speed and dbm access.
  52.  * We handle ihave & sendme in this process for dbm access and
  53.  * to work around syntax restrictions (<>).
  54.  *
  55.  * In future, one could pass header values to scripts as arguments or
  56.  * in environment, as NEWS* variables, to save time in the scripts.
  57.  */
  58. void
  59. ctlmsg(art)
  60. struct article *art;
  61. {
  62.     int pid, deadpid;
  63.     int wstatus;
  64.     char *inname = art->a_tmpf, *ctlcmd = art->h.h_ctlcmd;
  65.     static char nmcancel[] = "cancel ";
  66.     static char nmihave[] = "ihave ";
  67.     static char nmsendme[] = "sendme ";
  68.  
  69.     if (STREQN(ctlcmd, nmcancel, STRLEN(nmcancel))) {
  70.         art->a_status |= cancelart(ctlcmd + STRLEN(nmcancel));
  71.         return;
  72.     }
  73.     if (STREQN(ctlcmd, nmihave, STRLEN(nmihave))) {
  74.         ihave(ctlcmd + STRLEN(nmihave), art);
  75.         return;
  76.     }
  77.     if (STREQN(ctlcmd, nmsendme, STRLEN(nmsendme))) {
  78.         sendme(ctlcmd + STRLEN(nmsendme), art);
  79.         return;
  80.     }
  81.  
  82.     art->a_status |= synccaches();
  83.     (void) fflush(stdout);
  84.     (void) fflush(stderr);
  85.  
  86.     pid = fork();
  87.     if (pid == 0)                /* child? */
  88.         runctlmsg(ctlcmd, inname);
  89.     else if (pid == -1)
  90.         warning("fork failed", "");
  91.  
  92.     /* lint complains about &wstatus on 4.2+BSD; too bad, lint's wrong. */
  93.     while ((deadpid = wait(&wstatus)) != pid && deadpid != -1)
  94.         ;
  95.  
  96.     /* wrong kid returned, fork failed or child screwed up? */
  97.     if (deadpid == -1 || pid == -1 || wstatus != 0)
  98.         art->a_status |= ST_DROPPED;    /* admin got err.msg. by mail */
  99.     art->a_status |= loadcaches();
  100. }
  101.  
  102. STATIC boolean
  103. safecmd(cmd)            /* true if it's safe to system(3) cmd */
  104. char *cmd;
  105. {
  106.     register char *s;
  107.  
  108.     for (s = cmd; *s != '\0'; s++)
  109.         if (STREQN(s, "..", STRLEN("..")))
  110.             return NO;
  111.     for (s = SHELLMETAS; *s != '\0'; s++)
  112.         if (index(cmd, *s) != NULL)
  113.             return NO;
  114.     return YES;
  115. }
  116.  
  117. /*
  118.  * In theory (RFC 1036 nee 850), we should verify that the user issuing
  119.  * the cancel (the Sender: of this article or From: if no Sender) is the
  120.  * Sender: or From: of the original article or the local super-user.
  121.  *
  122.  * In practice, this is a lot of work and since anyone can forge news
  123.  * (and thus cancel anything), not worth the effort.
  124.  *
  125.  * Ignore ST_ACCESS while cancelling an already-seen article since the
  126.  * article may have been cancelled before or may have a fake history entry
  127.  * because the cancel arrived before the article.
  128.  *
  129.  * If the article being cancelled has not been seen yet, generate a history
  130.  * file entry for the cancelled article in case it arrives after the cancel
  131.  * control.  The history file entry will cause the cancelled article to be
  132.  * rejected as a duplicate.
  133.  */
  134. STATIC statust
  135. cancelart(msgidstr)
  136. char *msgidstr;
  137. {
  138.     register char *wsp;
  139.     register char *msgid = strsave(msgidstr);
  140.     register int idbytes;
  141.     register char *wholemsgid = msgid;
  142.     register statust status = ST_OKAY;
  143.  
  144.     /* skip leading whitespace in msgid */
  145.     while (*msgid != '\0' && isascii(*msgid) && isspace(*msgid))
  146.         ++msgid;
  147.     /* replace trailing whitespace with NULs; `wsp >= msgid' is not safe */
  148.     idbytes = strlen(msgid);
  149.     for (wsp = msgid + idbytes - 1; idbytes-- > 0 &&
  150.         isascii(*wsp) && isspace(*wsp); --wsp)
  151.         *wsp = '\0';
  152.  
  153.     if (alreadyseen(msgid)) {
  154.         register char *histent, *filelist;
  155.  
  156.         histent = gethistory(msgid);
  157.         if (histent != NULL && (filelist = findfiles(histent)) != NULL)
  158.             status |= snufffiles(filelist) & ~ST_ACCESS;
  159.     } else {
  160.         status |= fakehist(msgid, DEFEXP, NO_FILES);    /* start log */
  161.         (void) putchar('\n');        /* end log line */
  162.     }
  163.     free(wholemsgid);
  164.     return status;
  165. }
  166.  
  167. /*
  168.  * Execute a non-builtin control message by searching $NEWSCTL/bin and
  169.  * $NEWSBIN/ctl for the command named by the control message.
  170.  * runctlmsg is called from a child of relaynews, so it must always
  171.  * call _exit() rather than exit() to avoid flushing stdio buffers.
  172.  *
  173.  * Enforce at least minimal security: the environment was standardised at
  174.  * startup, including PATH and IFS; close non-standard file descriptors;
  175.  * reject shell metacharacters in ctlcmd.
  176.  */
  177. STATIC void
  178. runctlmsg(ctlcmd, inname)            /* child process */
  179. register char *ctlcmd, *inname;
  180. {
  181.     register char *cmd;
  182.     register int cmdstat;
  183.  
  184.     closeall(1);
  185.     if (!safecmd(ctlcmd)) {
  186.         (void) fprintf(stderr,
  187.             "%s: control `%s' looks unsafe to execute\n", progname, ctlcmd);
  188.         _exit(1);
  189.     }
  190.     cmd = malloc((unsigned) STRLEN("PATH=") + strlen(ctlfile("bin")) +
  191.         STRLEN(":") + strlen(SUBDIR) + STRLEN(";") + strlen(ctlcmd) +
  192.         STRLEN(" <") + strlen(inname) + 1);
  193.     if (cmd == NULL) {
  194.         warning("can't allocate memory in runctlmsg", "");
  195.         _exit(1);
  196.     }
  197.     (void) strcpy(cmd, "PATH=");
  198.     (void) strcat(cmd, ctlfile("bin"));
  199.     (void) strcat(cmd, ":");
  200.     (void) strcat(cmd, SUBDIR);
  201.     (void) strcat(cmd, ";");
  202.     (void) strcat(cmd, ctlcmd);
  203.     (void) strcat(cmd, " <");
  204.     (void) strcat(cmd, inname);
  205.  
  206.     cmdstat = system(cmd);
  207.     if (cmdstat != 0)
  208.         bombctlmsg(cmd, cmdstat);
  209.     free(cmd);
  210.     _exit(0);
  211. }
  212.  
  213. /*
  214.  * Notify the local news administrator by mail that "cmd" failed
  215.  * with "cmdstat" status, and _exit with bad status (again avoid stdio
  216.  * buffer flushing in the child).
  217.  */
  218. STATIC void
  219. bombctlmsg(cmd, cmdstat)
  220. char *cmd;
  221. int cmdstat;
  222. {
  223.     register char *mailcmd;
  224.     register FILE *mailf;
  225.     
  226.     mailcmd = malloc((unsigned)STRLEN("PATH=") + strlen(newspath()) +
  227.         STRLEN(" mail ") + strlen(newsmaster()) + 1);
  228.     if (mailcmd == NULL) {
  229.         warning("can't allocate memory in bombctlmsg", "");
  230.         _exit(1);
  231.     }
  232.     (void) sprintf(mailcmd, "PATH=%s mail %s", newspath(), newsmaster());
  233.     mailf = popen(mailcmd, "w");
  234.     if (mailf == NULL)
  235.         mailf = stderr;
  236.     (void) fprintf(mailf,
  237.         "%s: control message `%s' exited with status 0%o\n",
  238.         progname, cmd, cmdstat);
  239.     if (mailf != stderr)
  240.         (void) pclose(mailf);
  241.     free(mailcmd);
  242.     _exit(1);
  243. }
  244.